<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <script>
        // instanceof的实例方法检测 arr instanceof Array
        // 构造函数 arr.construtor == Array
        // typeof方法判断简单数据类型typeof[12]
        // 精确判断 Object.prototype.toString.call([56,23])

        // new > 实例化对象
        // prototype 原型，标准
        // constructor 构造函数
        // proto 原型 
        // extend 拓展，延伸   

        // new做了什么？
        // 1 会在内存中创建一个空对象
        // 2 设置构造函数的this，让this指向刚刚创建好的对象
        // 3 执行构造函数中的代码
        // 4 返回对象2
        // 5 这个新的空对象的__proto__指向构造函数的原型

        // 通过Student构造函数，创建的对象，可以访问Student.prototype中的成员
        // Student.prototype.constructor  指向了构造函数本身
        // 一般情况下，对象的属性在构造函数中来设置
        // 一般情况下，对象的方法在构造函数的原型对象中来设置
        // 可以重新改变原型对象的prototype属性，设置为一个新的对象
        // 当我们改变构造函数的prototype的时候，需要重新设置constructor属性
        // 使用对象访问原型对象中的constructor  是创建对象所使用的构造函数
        // 先去设置原型属性，再创建对象，才可以访问原型对象中的成员


        // 面向对象三大特征：封装，继承，多态
        // 复制对象的成员给另一个对象
        function extend(parent, child) {
            for (var key in parent) {
                if (child[key]) {
                    continue;
                }
                child[key] = parent[key];
            }
        }
        // 原型继承： 无法设置构造函数的参数
        // bind()   改变函数的this，并且返回一个新的函数  (不调用函数)
        // call()  改变函数中的this，直接调用函数
        // 组合继承：借用构造函数 + 原型继承
        // 通过原型，让子类型，继承父类型中的方法
        Student.prototype = new Person();
        Student.prototype.constructor = Student;
        // 通过原型让子类型继承父类型中的方法
        Teacher.prototype = new Person();
        Teacher.prototype.constructor = Teacher;

        // 每一个构造函数(以下简称Fn)都有一个原型，
        // 每个new Fn出来的实例对象 都会指向这个原型，所以无论new Fn出来多少个实例对象 
        // 都会同时指向这个原型，所以我们可以在原型上写一些公共的方法 解决 构造函数的缺陷

        // 注意： 一定要记住理解下面2句话
        // 为什么会使用原型？？？
        // 因为 1. 构造函数中的所有属性和方法都是复制 但是事实中 我们只需要复制属性 方法最好是公用一个
        // 因为 2. 原型对象中的方法只要一份 所有实例对象都会去到原型上找

        // 结论: 属性放在构造函数中  方法放在原型中  这便是我们的 组合式设计模式 (构造函数 + 原型)

        // instanceof 判断某个对象是否是某个构造函数的实例对象

        // 通过Student构造函数，创建的对象，可以访问Student.prototype中的成员
        // 当调用对象的属性或者方法的时候，先去找对象本身的属性/方法 ，
        // 如果对象没有该属性或者方法。此时去调用原型中的属性/方法
        // 如果对象本身没有该属性/方法，原型中也没有该属性或者方法，此时会报错
        // s1.__proto__  对象的__proto__ 等于  构造函数的Student.prototype
        // __proto__属性是非标准的属性

        // 在原型对象中有一个属性 constructor  构造函数
        // constructor  作用记录了创建该对象的构造函数  记录了创建该对象的构造函数
        // 函数内部的this，是有函数调用的时候来确定其指向的

        
        // 现代浏览器，不会提升if语句中的函数声明
        // 老的IE版本中，if语句中的函数声明会提升

        // 函数的调用形式
        // 1，普通函数调用   this指向window
        function fn() {

        }
        fn();
        // 2，方法调用   this指向调用该方法的对象
        var obj = {
            fn: function () {

            }
        }
        obj.fn();
        // 3，作为构造函数调用   构造函数内部的this指向由该构造函数创建的对象
        // 4，作为事件的处理函数   this指向触发该事件的对象
        btn.onclick = function () {
            console.log(this);
        }
        // 5，作为定时器的参数   this指向window
        setInterval(function () {

        }, 1000);
        // 终极总结：函数内部的this，是由函数调用的时候来确定其指向的

        call
        // 1，调用函数，改变函数中的this
        // 2，第一个参数，设置函数内部this的指向，其他参数对应函数的参数
        // 3，函数的返回值，call的返回值就是函数的返回值
        // 4，测试
        apply
        // 1，调用函数，改变函数中的this
        // 2，第一个参数，设置函数内部this的指向，第二个参数是数组
        // 3，函数的返回值，call的返回值就是函数的返回值
        // 4，测试
        bind
        // 1，改变函数中的this，不会调用函数，而是把函数复制一份
        // 2，第一个参数，设置函数内部this的指向，其他参数对应函数的参数
        // 3，函数的返回值，call的返回值就是函数的返回值
        // 4，测试
        var obj = function () {}.bind()
        

        // 闭包就是一个函数中返回一个函数，使我们可以在一个作用域中访问另一个作用域的变量
        // 闭包的缺点：常驻内存不会被释放掉

        JSON.stringify  // 将js对象转换成json字符串
        JSON.parse      // 将json字符串转化为js对象
        
        //   元字符
        //   \d   匹配数字
        //   \D   匹配任意非数字的字符
        //   \w   匹配字母或数字或下划线
        //   \W   匹配任意不是字母，数字，下划线
        //   \s   匹配任意的空白符
        //   \S   匹配任意不是空白符的字符
        //   .    匹配除换行符以外的任意单个字符
        //   ^    表示匹配行首的文本（以谁开始）
        //   $    表示匹配行尾的文本（以谁结束）

        //   限定符
        //   *       重复零次或更多次
        //   +       重复一次或更多次
        //   ？      重复零次或一次
        //   {n}     重复n次
        //   {n,}    重复n次或更多次
        //   {n,m}   重复n到m次 

        //   []字符串用中括号括起来，表示匹配其中的任一字符，相当于或的意思

        // 1 验证邮编    100010
        //  ^[1-9]\d{5}$

        // 2 验证手机号  18910000000
        // ^[1-9]\d{10}$

        // 3 验证日期    2012-5-01
        // ^\d{4}-\d{1,2}-\d{1,2}$

        // 4 验证邮箱    xxx@itcast.cn
        // ^\w+@\w+(\.\w+)+$
        // ^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$   

        // 5 验证IP地址  192.168.1.10
        // \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}
        




        //   一、校验数字的表达式
        //   数字：^[0-9]*$
        //   n位的数字：^\d{n}$
        //   至少n位的数字：^\d{n,}$
        //   m-n位的数字：^\d{m,n}$
        //   零和非零开头的数字：^(0|[1-9][0-9]*)$
        //   非零开头的最多带两位小数的数字：^([1-9][0-9]*)+(.[0-9]{1,2})?$
        //   带1-2位小数的正数或负数：^(\-)?\d+(\.\d{1,2})?$
        //   正数、负数、和小数：^(\-|\+)?\d+(\.\d+)?$
        //   有两位小数的正实数：^[0-9]+(\.[0-9]{2})?$
        //   有1~3位小数的正实数：^[0-9]+(\.[0-9]{1,3})?$
        //   非零的正整数：^[1-9]\d*$ 或 ^([1-9][0-9]*){1,3}$ 或 ^\+?[1-9][0-9]*$
        //   非零的负整数：^\-[1-9][]0-9"*$ 或 ^-[1-9]\d*$
        //   非负整数：^\d+$ 或 ^[1-9]\d*|0$
        //   非正整数：^-[1-9]\d*|0$ 或 ^((-\d+)|(0+))$
        //   非负浮点数：^\d+(\.\d+)?$ 或 ^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$
        //   非正浮点数：^((-\d+(\.\d+)?)|(0+(\.0+)?))$ 或 ^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$
        //   正浮点数：^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$ 或 ^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$
        //   负浮点数：^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$ 或 ^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$
        //   浮点数：^(-?\d+)(\.\d+)?$ 或 ^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$
        //   校验字符的表达式
        //   汉字：^[\u4e00-\u9fa5]{0,}$
        //   英文和数字：^[A-Za-z0-9]+$ 或 ^[A-Za-z0-9]{4,40}$
        //   长度为3-20的所有字符：^.{3,20}$
        //   由26个英文字母组成的字符串：^[A-Za-z]+$
        //   由26个大写英文字母组成的字符串：^[A-Z]+$
        //   由26个小写英文字母组成的字符串：^[a-z]+$
        //   由数字和26个英文字母组成的字符串：^[A-Za-z0-9]+$
        //   由数字、26个英文字母或者下划线组成的字符串：^\w+$ 或 ^\w{3,20}$
        //   中文、英文、数字包括下划线：^[\u4E00-\u9FA5A-Za-z0-9_]+$
        //   中文、英文、数字但不包括下划线等符号：^[\u4E00-\u9FA5A-Za-z0-9]+$ 或 ^[\u4E00-\u9FA5A-Za-z0-9]{2,20}$
        //   可以输入含有^%&',;=?$\"等字符：[^%&',;=?$\x22]+
        //   禁止输入含有~的字符：[^~\x22]+
        //   三、特殊需求表达式
        //   Email地址：^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$
        //   域名：[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/.?
        //   InternetURL：[a-zA-z]+://[^\s]* 或 ^http://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$
        //   手机号码：^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$
        //   电话号码("XXX-XXXXXXX"、"XXXX-XXXXXXXX"、"XXX-XXXXXXX"、"XXX-XXXXXXXX"、"XXXXXXX"和"XXXXXXXX)：^(\(\d{3,4}-)|\d{3.4}-)?\d{7,8}$
        //   国内电话号码(0511-4405222、021-87888822)：\d{3}-\d{8}|\d{4}-\d{7}
        //   电话号码正则表达式（支持手机号码，3-4位区号，7-8位直播号码，1－4位分机号）: ((\d{11})|^((\d{7,8})|(\d{4}|\d{3})-(\d{7,8})|(\d{4}|\d{3})-(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1})|(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1}))$)
        //   身份证号(15位、18位数字)，最后一位是校验位，可能为数字或字符X：(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)
        //   帐号是否合法(字母开头，允许5-16字节，允许字母数字下划线)：^[a-zA-Z][a-zA-Z0-9_]{4,15}$
        //   密码(以字母开头，长度在6~18之间，只能包含字母、数字和下划线)：^[a-zA-Z]\w{5,17}$
        //   强密码(必须包含大小写字母和数字的组合，不能使用特殊字符，长度在8-10之间)：^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,10}$
        //   日期格式：^\d{4}-\d{1,2}-\d{1,2}
        //   一年的12个月(01～09和1～12)：^(0?[1-9]|1[0-2])$
        //   一个月的31天(01～09和1～31)：^((0?[1-9])|((1|2)[0-9])|30|31)$
        //   钱的输入格式：
        //   有四种钱的表示形式我们可以接受:"10000.00" 和 "10,000.00", 和没有 "分" 的 "10000" 和 "10,000"：^[1-9][0-9]*$
        //   这表示任意一个不以0开头的数字,但是,这也意味着一个字符"0"不通过,所以我们采用下面的形式：^(0|[1-9][0-9]*)$
        //   一个0或者一个不以0开头的数字.我们还可以允许开头有一个负号：^(0|-?[1-9][0-9]*)$
        //   这表示一个0或者一个可能为负的开头不为0的数字.让用户以0开头好了.把负号的也去掉,因为钱总不能是负的吧。下面我们要加的是说明可能的小数部分：^[0-9]+(.[0-9]+)?$
        //   必须说明的是,小数点后面至少应该有1位数,所以"10."是不通过的,但是 "10" 和 "10.2" 是通过的：^[0-9]+(.[0-9]{2})?$
        //   这样我们规定小数点后面必须有两位,如果你认为太苛刻了,可以这样：^[0-9]+(.[0-9]{1,2})?$
        //   这样就允许用户只写一位小数.下面我们该考虑数字中的逗号了,我们可以这样：^[0-9]{1,3}(,[0-9]{3})*(.[0-9]{1,2})?$
        //   1到3个数字,后面跟着任意个 逗号+3个数字,逗号成为可选,而不是必须：^([0-9]+|[0-9]{1,3}(,[0-9]{3})*)(.[0-9]{1,2})?$
        //   备注：这就是最终结果了,别忘了"+"可以用"*"替代如果你觉得空字符串也可以接受的话(奇怪,为什么?)最后,别忘了在用函数时去掉去掉那个反斜杠,一般的错误都在这里
        //   xml文件：^([a-zA-Z]+-?)+[a-zA-Z0-9]+\\.[x|X][m|M][l|L]$
        //   中文字符的正则表达式：[\u4e00-\u9fa5]
        //   双字节字符：[^\x00-\xff] (包括汉字在内，可以用来计算字符串的长度(一个双字节字符长度计2，ASCII字符计1))
        //   空白行的正则表达式：\n\s*\r (可以用来删除空白行)
        //   HTML标记的正则表达式：<(\S*?)[^>]*>.*?|<.*? /> ( 首尾空白字符的正则表达式：^\s*|\s*$或(^\s*)|(\s*$) (可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等)，非常有用的表达式)
        //   腾讯QQ号：[1-9][0-9]{4,} (腾讯QQ号从10000开始)
        //   中国邮政编码：[1-9]\d{5}(?!\d) (中国邮政编码为6位数字)
        //   IP地址：((?:(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d))
       
        // split分离
        // var dateStr = '2015-1-5';
        // console.log(dateStr.split('-'));

        // var dateStr = '2018/5-23';
        // console.log(dateStr.split(/[/-]/));

        // var str = 'xxx@123abc.com';
        // console.log(str.split(/[@\.]/));

        // match提取
        // 1. 提取工资
        // var str = '张三：1000，李四：5000，王五：8000。';
        // var reg = /\d+/g;   (g:全局匹配   i:忽略大小写)
        // console.log(str.match(reg));

        // replace替换
        // var str = "abc,efg,123，abc,123，a";
        // console.log(str.replace(/,|，/g, '.'));
    </script>
</body>
</html>